package com.xiefengf.elasticsearch.es;

import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.elasticsearch.client.Node;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.time.Duration;

/**
 * @author xiefengf
 * @version 1.0
 * @date 2024/3/25 10:06
 * <p>
 * 我们在学习ES客户端时，一直使用的都是Java High Level Rest Client，在浏览官网时，发现官方给出的警告是：Java REST 客户端已被弃用，取而代之的是 Java API client 客户端，
 * ES 8.x 新版本中，Type 概念被弃用，所以新版 JavaAPI 也相应做出了改变，使用更加简便。
 * ES 官方从 7.15 起开始建议使用新的 JavaAPI
 * <p>
 * <p>
 * https://blog.csdn.net/weixin_45865102/article/details/127100989
 * https://blog.51cto.com/u_16213718/9453379
 */
@EnableElasticsearchRepositories(basePackages = {"com.xiefengf.elasticsearch.es","com.xiefengf.elasticsearch.jd"})
@Configuration
public class ElasticSearchConfig extends ElasticsearchConfigurationSupport {
    @Resource
    private ElasticsearchProperties elasticSearchProperty;

    @Bean(value = "elasticsearchTemplate")
    public ElasticsearchRestTemplate elasticsearchRestTemplate(@Qualifier("restHighLevelClient") RestHighLevelClient restHighLevelClient) {
        return new ElasticsearchRestTemplate(restHighLevelClient);
    }

    @Bean(value = "elasticsearchOperations")
    public ElasticsearchOperations elasticsearchTemplate(@Qualifier("restHighLevelClient") RestHighLevelClient restHighLevelClient) {
        return new ElasticsearchRestTemplate(restHighLevelClient);
    }

    @Bean(value = "restHighLevelClient")
    public RestHighLevelClient elasticsearchClient(@Qualifier("restClientBuilder") RestClientBuilder restClientBuilder) {
        return new RestHighLevelClient(restClientBuilder);
    }

    @Bean(value = "restClientBuilder")
    public RestClientBuilder restClientBuilder() {
        return RestClient.builder(elasticSearchProperty.getUris().stream().map(this::getElasticSearchHttpHosts).toArray(HttpHost[]::new)).
                setRequestConfigCallback(requestConfigBuilder -> {
                    //设置连接超时时间
                    requestConfigBuilder.setConnectTimeout((int) (elasticSearchProperty.getConnectionTimeout().toMillis() % 1000));
                    requestConfigBuilder.setSocketTimeout((int) (elasticSearchProperty.getSocketTimeout().toMillis() % 1000));
//                    requestConfigBuilder.setSocketTimeout(elasticSearchProperty.getSocketTimeout().toMillisPart());
                    return requestConfigBuilder;
                }).setFailureListener(new RestClient.FailureListener() {
                    //某节点失败,这里可以做一些告警
                    @Override
                    public void onFailure(Node node) {
//                        log.error("[ ElasticSearchClient ] >>  node :{}, host:{},  fail ", node.getName(), node.getHost());
                    }
                }).setHttpClientConfigCallback(httpClientBuilder -> {
                    httpClientBuilder.disableAuthCaching();
                    //设置账密
                    return getHttpAsyncClientBuilder(httpClientBuilder);
                });
    }

    /**
     * URL地址格式转换
     * http://124.220.215.81:9200 ---> 124.220.215.81:9200
     *
     * @param host
     * @return
     */
    private HttpHost getElasticSearchHttpHosts(String host) {
        host = host.replaceAll("http://", "").replaceAll("https://", "");
        return new HttpHost(host.split(":")[0], Integer.parseInt(host.split(":")[1]), "http");
    }

    /**
     * 设置账密
     *
     * @param httpClientBuilder
     * @return
     */
    private HttpAsyncClientBuilder getHttpAsyncClientBuilder(HttpAsyncClientBuilder httpClientBuilder) {
        // 如果为空，直接返回
        if (StringUtils.hasLength(elasticSearchProperty.getUsername()) || StringUtils.hasLength(elasticSearchProperty.getPassword())) {
            return httpClientBuilder;
        }
        // 设置账号密码
        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(elasticSearchProperty.getUsername(), elasticSearchProperty.getPassword()));
        httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
        //最大连接数
        httpClientBuilder.setMaxConnTotal(5);
        //每5分钟发生一次心跳保持连接
        httpClientBuilder.setKeepAliveStrategy((response, context) -> Duration.ofMinutes(5).toMillis());
        return httpClientBuilder;
    }
}